home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / DTS.Lib / TreeObj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-22  |  42.8 KB  |  1,730 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:     DTS.Lib
  5. ** File:        TreeObj.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1992 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12.  
  13.  
  14. #include "DTS.Lib2.h"
  15. #include "DTS.Lib.protos.h"
  16.  
  17. #ifndef __FILES__
  18. #include <Files.h>
  19. #endif
  20.  
  21. #ifndef __STDIO__
  22. #include <StdIO.h>
  23. #endif
  24.  
  25. #ifndef __STRING__
  26. #include <String.h>
  27. #endif
  28.  
  29. #ifndef __TREEOBJ__
  30. #include "TreeObj.h"
  31. #endif
  32.  
  33. #ifndef __UTILITIES__
  34. #include "Utilities.h"
  35. #endif
  36.  
  37.  
  38.  
  39. #define NEW_CHILD     1
  40. #define DISPOSE_CHILD 2
  41. #define MOVE_CHILD    3
  42. #define SWAP_CHILDREN 4
  43. #define CHANGE_CHILD  5
  44.  
  45.  
  46.  
  47. extern TreeObjProcPtr    gTreeObjMethods[];
  48. extern long                gMinTreeObjSize[];
  49.  
  50. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copyCNum);
  51. static OSErr        ReadBranch(TreeObjHndl hndl, short fileRefNum);
  52. static OSErr        ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  53. static OSErr        WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  54.  
  55. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType);
  56. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short cnum, TreeObjHndl dhndl, short dcnum, Boolean deepCopy);
  57.  
  58. static OSErr        PostNewChild(short editType, TreeObjHndl phndl, short cnum);
  59. static OSErr        PostDisposeChild(short editType, TreeObjHndl phndl, short cnum);
  60. static void            PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum);
  61. static void            PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb);
  62.  
  63. static void            UndoNewChild(TreeObjHndl undoPart);
  64. static void            UndoDisposeChild(TreeObjHndl undoPart);
  65. static void            UndoMoveChild(TreeObjHndl undoPart);
  66. static void            UndoModifyChild(TreeObjHndl undoPart);
  67. static void            UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy);
  68. static void            UndoSwapChildren(TreeObjHndl undoPart);
  69.  
  70.  
  71.  
  72. /**********************************************************************/
  73.  
  74.  
  75.  
  76. /* Creates an object with no parent.  This object will be the root object
  77. ** for a tree.  Returns nil upon failure. */
  78.  
  79. #pragma segment File
  80. TreeObjHndl    NewRootObj(short ctype, long size)
  81. {
  82.     TreeObjHndl        root;
  83.     TreeObjPtr        ptr;
  84.     char            *cptr;
  85.     short            i;
  86.     TreeObjProcPtr    proc;
  87.  
  88.     if (size < gMinTreeObjSize[ctype])
  89.         size = gMinTreeObjSize[ctype];
  90.             /* Ensure a minimally-sized object. */
  91.  
  92.     root = (TreeObjHndl)NewHandle(sizeof(TreeObj) + size);
  93.     if (!root) return(nil);
  94.         /* Oh well...
  95.         ** Note that since we are creating an orphan, it isn't possible to purge
  96.         ** old undos to possibly make space.  This means that for anyone calling
  97.         ** NewRootObj to get a handlewho considers it okay to purge old undos to
  98.         ** get the memory if necessary, has to do the following:
  99.         ** 1)  Call NewRootObj
  100.         ** 2)  If failure, call PurgeUndo for a particular document to purge the
  101.         **     oldest undo for that document to free up ram.
  102.         ** 3)  If PurgeUndo returns true, then something was purged and there is more
  103.         **     ram available.  Loop back to step 1 and try to create the object again.
  104.         ** 4)  If PurgeUndo returns false, then there really isn't any more ram.  One
  105.         **     possibility is to try purging undos from other documents, but this is
  106.         **     probably rude. */
  107.  
  108.     cptr = GetDataPtr(root);
  109.     for (i = 0; i < size; ++i) cptr[i] = 0;
  110.         /* Initialize data area to 0's.  This is very nice of us. */
  111.  
  112.     ptr = *root;                     /* Deref root object once.      */
  113.     ptr->type        = ctype;         /* Flag what object type it is. */
  114.     ptr->numChildren = 0;            /* Give root 0 children.        */
  115.     ptr->dataSize    = size;        /* Remember the data area size. */
  116.     ptr->treeID      = 0;            /* Requisite do-nothing field.  */
  117.     ptr->parent      = nil;            /* We aren't owned yet.         */
  118.  
  119.     if (proc = gTreeObjMethods[ctype]) {                /* If this object type has a proc...   */
  120.         if ((*proc)(root, INITMESSAGE, CREATEINIT)) {    /* Call the proc with an init message. */
  121.             DisposeHandle((Handle)root);                /* If the init complains, it's no go.  */
  122.             return(nil);
  123.         }
  124.     }
  125.  
  126.     return(root);    /* Success.  Return the handle. */
  127. }
  128.  
  129.  
  130.  
  131. /**********************************************************************/
  132.  
  133.  
  134.  
  135. /* Creates a child of the specified type and adds it to the parent at the
  136. ** specified location.  Returns nil upon failure. */
  137.  
  138. #pragma segment File
  139. TreeObjHndl    NewChild(short editType, TreeObjHndl phndl, short cnum, short ctype, long size)
  140. {
  141.     TreeObjHndl        chndl;
  142.     TreeObjProcPtr    proc;
  143.  
  144.     for (;;) {
  145.         chndl = NewRootObj(ctype, size);    /* Let somebody else do the creative work. */
  146.         if (chndl) break;                    /* We succeeded at creating an orphan. */
  147.         if (!PurgeUndo(phndl)) return(nil);
  148.             /* Oh well...
  149.             ** There is really no memory.  We even purged the undos to try to make
  150.             ** room, but we still didn't have enough ram to create the orphan object. */
  151.     };
  152.  
  153.     /* Now that we have successfully created an orphan object, make it someone's child. */
  154.  
  155.     GetChildHndlPtr(phndl, &cnum, 1);
  156.         /* Adjust cnum to within bounds.  See GetChildHndlPtr() for more info. */
  157.  
  158.     if (InsertChildHndl(phndl, chndl, cnum)) {
  159.             /* Couldn't insert the child into the parent handle table. */
  160.         if (proc = gTreeObjMethods[ctype])
  161.             (*proc)(chndl, FREEMESSAGE, 0);
  162.                 /* Since we couldn't attatch, call the object with a free message so that
  163.                 ** it can get rid of any additional memory that was allocated when an
  164.                 ** init message was sent to it. */
  165.         DisposeHandle((Handle)chndl);
  166.             /* After the free message, the object now consists of a single handle.
  167.             ** Dispose this handle and it is history completely. */
  168.         return(nil);
  169.     }
  170.  
  171.     if (PostNewChild(editType, phndl, cnum)) {
  172.         DisposeChild(NO_EDIT, phndl, cnum);
  173.         chndl = nil;
  174.     }
  175.  
  176.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  177.     return(chndl);
  178. }
  179.  
  180.  
  181.  
  182. /**********************************************************************/
  183.  
  184.  
  185.  
  186. /* Disposes of the specified child and all offspring of that child. */
  187.  
  188. #pragma segment File
  189. void    DisposeChild(short editType, TreeObjHndl phndl, short cnum)
  190. {
  191.     TreeObjHndl    chndl;
  192.  
  193.     if (GetChildHndlPtr(phndl, &cnum, 0)) {
  194.         /* This test checks that there are children in the table, plus it
  195.         ** also adjusts cnum to legit values, if possible. */
  196.  
  197.         chndl = GetChildHndl(phndl, cnum);
  198.         DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  199.  
  200.         if (editType) {
  201.             if (PostDisposeChild(editType, phndl, cnum)) {
  202.                     /* Posting a DisposeChild can actually take more ram.
  203.                     ** The reason for this is that the object isn't disposed of.
  204.                     ** It is moved into the undo, plus information as to where to
  205.                     ** put it back is kept.  If PostDisposeChild fails, then we
  206.                     ** are in a bad way memory-wise, so we really need to let the
  207.                     ** dispose occur.  To accomplish this, we disable undos for
  208.                     ** this document, and then we go ahead and allow a straight
  209.                     ** dispose of the child.  This will get the job done, plus it
  210.                     ** will free up some ram.
  211.                     ** The reason that undos are disabled is that the edit that
  212.                     ** is occuring may have multiple operations.  We want to stop
  213.                     ** collection of the remaining operations for this edit, as
  214.                     ** it wouldn't be a complete undo anyway.  The undos will
  215.                     ** be enabled again when NewUndo is called to start a new edit. */
  216.                 DisableUndo(phndl);
  217.                     /* Undo collection off temporarily, plus all undos are purged,
  218.                     ** thus freeing up ram. */
  219.                 editType = 0;
  220.             }
  221.         }
  222.  
  223.         if (!editType) {
  224.             DisposeObjAndOffspring(GetChildHndl(phndl, cnum));
  225.             *GetChildHndlPtr(phndl, &cnum, 0) = nil;
  226.                 /* Since the child no longer exists, DeleteChildHndl can't set
  227.                 ** the parent reference for the child to nil.  Setting the reference
  228.                 ** to the child prevents DeleteChildHndl from doing this. */
  229.             DeleteChildHndl(phndl, cnum);
  230.         }
  231.     }
  232. }
  233.  
  234.  
  235.  
  236. /**********************************************************************/
  237.  
  238.  
  239.  
  240. /* Copies the specified child (and all offspring of that child if deepCopy is true). */
  241.  
  242. #pragma segment File
  243. TreeObjHndl    CopyChild(short editType, TreeObjHndl shndl, short scnum,
  244.                       TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  245. {
  246.     TreeObjHndl    chndl, copyHndl;
  247.  
  248.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(nil);
  249.         /* Adjust scnum to within bounds, if possible. */
  250.  
  251.     GetChildHndlPtr(dhndl, &dcnum, 1);
  252.         /* Adjust dcnum to within bounds. */
  253.  
  254.     copyHndl = CopyOneChild(chndl = GetChildHndl(shndl, scnum), dhndl, dcnum);
  255.     if (!copyHndl) return(nil);
  256.  
  257.     if (deepCopy) {
  258.         if (CopyChildren(chndl, copyHndl)) {        /* Copy child's children. */
  259.             DisposeChild(NO_EDIT, dhndl, dcnum);
  260.             return(nil);
  261.         }
  262.     }
  263.  
  264.     if (PostNewChild(editType, dhndl, dcnum)) {
  265.         DisposeChild(NO_EDIT, dhndl, dcnum);
  266.         return(nil);
  267.     }
  268.  
  269.     DoTreeObjMethod(copyHndl, UNDOMESSAGE, UNDOTODOC);
  270.     return(copyHndl);
  271. }
  272.  
  273.  
  274.  
  275. /**********************************************************************/
  276.  
  277.  
  278.  
  279. /* Moves a child from one location on the tree to another. */
  280.  
  281. #pragma segment File
  282. OSErr    MoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  283. {
  284.     TreeObjHndl    chndl;
  285.     OSErr        err;
  286.  
  287.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(paramErr);
  288.         /* Adjust scnum to within bounds.  Adjustment not possible if no children. */
  289.  
  290.     GetChildHndlPtr(dhndl, &dcnum, 1);        /* Adjust dcnum to within bounds. */
  291.  
  292.     chndl = GetChildHndl(shndl, scnum);
  293.  
  294.     if (err = InsertChildHndl(dhndl, chndl, dcnum)) return(err);
  295.         /* See if the child can be moved.  If no space at new location, return error. */
  296.  
  297.     DeleteChildHndl(dhndl, dcnum);
  298.         /* The child will be able to be moved, so remove the test addition.
  299.         ** It is important to have the child detached from the tree, as the new
  300.         ** location it is to be moved to is an index based on that the child
  301.         ** isn't attached yet.  This only matters if the child is being moved
  302.         ** to a new location for the same parent. */
  303.  
  304.     DeleteChildHndl(shndl, scnum);
  305.     GetChildHndlPtr(dhndl, &dcnum, 1);
  306.         /* Adjust dcnum to within bounds.  It may be just out of bounds if shndl
  307.         ** and dhndl are the same.  After the DeleteChildHndl, dcnum may be 2 past
  308.         ** the end, instead of the allowable 1 for a target. */
  309.  
  310.     InsertChildHndl(dhndl, chndl, dcnum);
  311.  
  312.     PostMoveChild(editType, shndl, scnum, dhndl, dcnum);
  313.     return(noErr);
  314. }
  315.  
  316.  
  317.  
  318. /**********************************************************************/
  319.  
  320.  
  321.  
  322. /* Saves a copy of the child in the undo hierarchy for undo/redo purposes. */
  323.  
  324. #pragma segment File
  325. OSErr    ModifyChild(short editType, TreeObjHndl phndl, short cnum, Boolean deepCopy)
  326. {
  327.     TreeObjHndl    undo, task, part, copy;
  328.     short        i;
  329.     OSErr        err;
  330.  
  331.     if (!editType) return(noErr);
  332.  
  333.     err  = noErr;
  334.     copy = nil;
  335.     undo = GetUndoHndl(phndl);
  336.  
  337.     if (mDerefUndo(undo)->disabled)
  338.         copy = phndl;        /* Used for flag purposes. */
  339.  
  340.     else {
  341.         if (task = GetUndoTaskHndl(undo, editType)) {
  342.             for (i = (*task)->numChildren; i;) {
  343.                 part = GetChildHndl(task, --i);
  344.                 if (mDerefUndoPart(part)->actionType == CHANGE_CHILD)
  345.                     if ((mDerefUndoPart(part)->shndl ==phndl) && (mDerefUndoPart(part)->scnum ==cnum))
  346.                         return(noErr);
  347.                             /* Child is already posted as changed for this undo editType. */
  348.             }
  349.  
  350.             part = NewUndoPart(task, CHANGE_CHILD, phndl, cnum, nil, 0, deepCopy);
  351.             if (part)
  352.                 copy = CopyChild(NO_EDIT, phndl, cnum, part, -1, deepCopy);
  353.         }
  354.     }
  355.  
  356.     if (!copy) {
  357.         DisableUndo(phndl);
  358.         err = memFullErr;
  359.     }
  360.  
  361.     return(err);
  362. }
  363.  
  364.  
  365.  
  366. /**********************************************************************/
  367.  
  368.  
  369.  
  370. /* Swaps the child handles. */
  371.  
  372. #pragma segment File
  373. OSErr    SwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  374. {
  375.     TreeObjHndl    *tptra, *tptrb, hndl;
  376.  
  377.     if (!(tptra = GetChildHndlPtr(hndla, &cnuma, 0))) return(paramErr);
  378.     if (!(tptrb = GetChildHndlPtr(hndlb, &cnumb, 0))) return(paramErr);
  379.  
  380.     hndl = *tptra;
  381.     *tptra = *tptrb;
  382.     *tptrb = hndl;
  383.  
  384.     (*GetChildHndl(hndla, cnuma))->parent = hndla;
  385.     (*GetChildHndl(hndlb, cnumb))->parent = hndlb;
  386.  
  387.     PostSwapChildren(editType, hndla, cnuma, hndlb, cnumb);
  388.     return(noErr);
  389. }
  390.  
  391.  
  392.  
  393. /**********************************************************************/
  394.  
  395.  
  396.  
  397. /* Swaps the child data without swapping the handles. */
  398.  
  399. #pragma segment File
  400. OSErr    SwapChildData(TreeObjHndl hndla, TreeObjHndl hndlb)
  401. {
  402.     TreeObjHndl    hndl;
  403.     long        size, sizea, sizeb, i;
  404.     char        *ptra, *ptrb, c;
  405.     OSErr        err;
  406.  
  407.     sizea = (*hndla)->dataSize;
  408.     sizeb = (*hndlb)->dataSize;
  409.     if (sizea > sizeb) {        /* Make hndla the small one and hndlb the big one. */
  410.         size  = sizea;
  411.         sizea = sizeb;
  412.         sizeb = size;
  413.         hndl  = hndla;
  414.         hndla = hndlb;
  415.         hndlb = hndl;
  416.     }
  417.  
  418.     err = SetDataSize(hndla, sizeb);        /* Make the small one big. */
  419.     if (!err) {
  420.         ptra = GetDataPtr(hndla);
  421.         ptrb = GetDataPtr(hndlb);
  422.         for (i = 0; i < sizeb; ++i) {
  423.             c = ptra[i];
  424.             ptra[i] = ptrb[i];
  425.             ptrb[i] = c;
  426.         }
  427.         SetDataSize(hndlb, sizea);            /* Make the big one small. */
  428.     }
  429.  
  430.     return(err);
  431. }
  432.  
  433.  
  434.  
  435. /**********************************************************************/
  436. /**********************************************************************/
  437.  
  438.  
  439.  
  440. /* This disposes of the child and any offspring of that child.  It does not remove
  441. ** the child from the parent's child handle table. */
  442.  
  443. #pragma segment File
  444. void    DisposeObjAndOffspring(TreeObjHndl chndl)
  445. {
  446.     short            nc;
  447.     TreeObjProcPtr    proc;
  448.  
  449.     if (!chndl) return;
  450.  
  451.     if (proc = gTreeObjMethods[(*chndl)->type])
  452.         (*proc)(chndl, FREEMESSAGE, 0);
  453.             /* If the object has any additional deallocation to do, let it do it. */
  454.  
  455.     while (nc = (*chndl)->numChildren) {
  456.         DisposeObjAndOffspring(GetChildHndl(chndl, nc - 1));
  457.         (*chndl)->numChildren--;
  458.     }
  459.     DisposeHandle((Handle)chndl);
  460. }
  461.  
  462.  
  463.  
  464. /**********************************************************************/
  465.  
  466.  
  467.  
  468. /* Copies the children (and children below that and so on) of one object to
  469. ** another object.  Used internally by CopyChild for deep copies. */
  470.  
  471. #pragma segment File
  472. OSErr    CopyChildren(TreeObjHndl shndl, TreeObjHndl dhndl)
  473. {
  474.     TreeObjHndl    chndl, copyHndl;
  475.     short    i;
  476.     OSErr    err;
  477.  
  478.     for (i = (*shndl)->numChildren; i;) {
  479.         chndl    = GetChildHndl(shndl, --i);
  480.         copyHndl = CopyOneChild(chndl, dhndl, 0);
  481.         if (!copyHndl) return(memFullErr);
  482.         if (err = CopyChildren(chndl, copyHndl)) return(err);
  483.     }
  484.     return(noErr);
  485. }
  486.  
  487.  
  488.  
  489. /**********************************************************************/
  490.  
  491.  
  492.  
  493. /* Used internally by CopyChild for shallow copies. */
  494.  
  495. #pragma segment File
  496. TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copycnum)
  497. {
  498.     TreeObjHndl        copyHndl;
  499.     short            type;
  500.     long            size;
  501.     TreeObjProcPtr    proc;
  502.  
  503.     type = (*chndl)->type;
  504.     size = (*chndl)->dataSize;
  505.  
  506.  
  507.     proc = gTreeObjMethods[type];    /* Prevent NewChild() from calling the */
  508.     gTreeObjMethods[type] = nil;    /* object for INITMESSAGE. */
  509.  
  510.     copyHndl = NewChild(NO_EDIT, copyToHndl, copycnum, type, size);
  511.         /* NewChild takes care of bounds-checking for copycnum.  The child data has
  512.         ** not been fully initialized, as we prevented the INITMESSAGE. */
  513.  
  514.     gTreeObjMethods[type] = proc;    /* Re-enable messaging the object. */
  515.  
  516.     if (!copyHndl) return(nil);
  517.  
  518.     BlockMove(GetDataPtr(chndl), GetDataPtr(copyHndl), size);
  519.     if (proc) {
  520.         if ((*proc)(copyHndl, COPYMESSAGE, (long)chndl)) {
  521.             DisposeChild(NO_EDIT, copyHndl, copycnum);
  522.             return(nil);
  523.         }
  524.     }
  525.  
  526.     return(copyHndl);
  527. }
  528.  
  529.  
  530.  
  531. /**********************************************************************/
  532. /**********************************************************************/
  533.  
  534.  
  535.  
  536. /* Adds an existing child to a parent's child handle table. */
  537.  
  538. #pragma segment File
  539. OSErr    InsertChildHndl(TreeObjHndl phndl, TreeObjHndl chndl, short cnum)
  540. {
  541.     TreeObjHndl    *tptr;
  542.     TreeObjPtr    ptr;
  543.     long        oldSize, newSize, dhSize;
  544.     long        oldTblSize, tblOffset;
  545.     OSErr        err;
  546.  
  547.     oldSize = GetHandleSize((Handle)phndl);
  548.     dhSize  = sizeof(TreeObj) + (ptr = *phndl)->dataSize;        /* Data + header size. */
  549.  
  550.     oldTblSize = ptr->numChildren * sizeof(TreeObjHndl);
  551.  
  552.     newSize = dhSize + oldTblSize + sizeof(TreeObjHndl);
  553.     newSize |= 0x1F;
  554.     ++newSize;
  555.  
  556.     if (newSize > oldSize) {
  557.         SetHandleSize((Handle)phndl, newSize);
  558.         if (err = MemError()) return(err);
  559.         ptr = *phndl;
  560.     }
  561.  
  562.     tptr      = GetChildHndlPtr(phndl, &cnum, 1);
  563.     tblOffset = cnum * sizeof(TreeObjHndl);
  564.     BlockMove((char *)tptr, (char *)(tptr + 1), oldTblSize - tblOffset);
  565.  
  566.     ptr->numChildren++;
  567.     *tptr = chndl;
  568.     (*chndl)->parent = phndl;
  569.     return(noErr);
  570. }
  571.  
  572.  
  573.  
  574. /**********************************************************************/
  575.  
  576.  
  577.  
  578. /* Removes a child from the parent's child handle table. */
  579.  
  580. #pragma segment File
  581. void    DeleteChildHndl(TreeObjHndl phndl, short cnum)
  582. {
  583.     TreeObjHndl    *tptr;
  584.     TreeObjHndl    chndl;
  585.     TreeObjPtr    ptr;
  586.     long        dhSize, tblSize, tblOffset;
  587.  
  588.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return;
  589.     chndl = *tptr;
  590.  
  591.     dhSize    = sizeof(TreeObj) + (ptr = *phndl)->dataSize;
  592.     tblSize   = ptr->numChildren * sizeof(TreeObjHndl);
  593.     tblOffset = cnum * sizeof(TreeObjHndl);
  594.  
  595.     BlockMove((char *)(tptr + 1), (char *)tptr, tblSize - tblOffset - sizeof(TreeObjHndl));
  596.     SetHandleSize((Handle)phndl, ((dhSize + tblSize - sizeof(TreeObjHndl)) | 0x1F) + 1);
  597.     (*phndl)->numChildren--;
  598.  
  599.     if (chndl)
  600.         (*chndl)->parent = nil;
  601. }
  602.  
  603.  
  604.  
  605. /**********************************************************************/
  606.  
  607.  
  608.  
  609. /* Given an object handle, return the root handle. */
  610.  
  611. #pragma segment File
  612. TreeObjHndl    GetRootHndl(TreeObjHndl hndl)
  613. {
  614.     for (; (*hndl)->parent; hndl = (*hndl)->parent);
  615.     return(hndl);
  616. }
  617.  
  618.  
  619.  
  620. /**********************************************************************/
  621.  
  622.  
  623.  
  624. /* Given a parent handle and a child number, this returns the child handle. */
  625.  
  626. #pragma segment File
  627. TreeObjHndl    GetChildHndl(TreeObjHndl phndl, short cnum)
  628. {
  629.     TreeObjHndl    *tptr;
  630.  
  631.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return(nil);
  632.     return(*tptr);
  633. }
  634.  
  635.  
  636.  
  637. /**********************************************************************/
  638.  
  639.  
  640.  
  641. /* Return a pointer into the child handle table.  This also validates (and corrects)
  642. ** cnum so that it is in range, if possible.  Depending on the usage, pointing to
  643. ** just after the child handle table is either valid or invalid.  If a handle is
  644. ** being added to the table, then pointing just after the table is valid.  If a
  645. ** handle is being removed or referenced, then pointing just after the table is
  646. ** invalid.  the parameter endCase determines which case we are dealing with.  If
  647. ** endCase is 0, then pointing just after the child handle table is invalid, and
  648. ** if the cnum value passed in causes this, then nil is returned for the pointer.
  649. ** if endCase is 1, then pointing just after the child handle table is okay, and
  650. ** therefore nil will never be returned as the pointer.  Any cnum value out of
  651. ** range will be corrected (if possible) to be within range. */
  652.  
  653. #pragma segment File
  654. TreeObjHndl    *GetChildHndlPtr(TreeObjHndl phndl, short *cnum, short endCase)
  655. {
  656.     TreeObjPtr    ptr;
  657.     short        nc;
  658.     long        dhSize, tblOffset;
  659.  
  660.     ptr = *phndl;
  661.     if (!((nc = ptr->numChildren) + endCase)) return(nil);
  662.  
  663.     if ((*cnum < 0) || (*cnum >= nc + endCase))
  664.         *cnum = nc - 1 + endCase;
  665.  
  666.     dhSize    = sizeof(TreeObj) + ptr->dataSize;
  667.     tblOffset = *cnum * sizeof(TreeObjHndl);
  668.     return((TreeObjHndl *)((char *)ptr + dhSize + tblOffset));
  669. }
  670.  
  671.  
  672.  
  673. /**********************************************************************/
  674.  
  675.  
  676.  
  677. /* Given a child handle, this returns the child number of that
  678. ** handle in the parent's child handle table. */
  679.  
  680. #pragma segment File
  681. short    GetChildNum(TreeObjHndl hndl)
  682. {
  683.     TreeObjHndl    phndl, *tptr;
  684.     short        nc, j;
  685.  
  686.     if (!(phndl = (*hndl)->parent)) return(-1);
  687.         /* Child doesn't have a parent, and therefore it is a root. */
  688.  
  689.     j = 0;
  690.     if (tptr = GetChildHndlPtr(phndl, &j, 0)) {
  691.         nc  = (*phndl)->numChildren;
  692.         for (; j < nc; ++j) if (*tptr++ == hndl) return(j);
  693.     }
  694.  
  695.     return(-1);
  696. }
  697.  
  698.  
  699.  
  700. /**********************************************************************/
  701. /**********************************************************************/
  702.  
  703.  
  704.  
  705. /* Adjusts the data size, either greater or smaller, depending on the sign of 'delta'. */
  706.  
  707. #pragma segment File
  708. OSErr    AdjustDataSize(TreeObjHndl hndl, long delta)
  709. {
  710.     TreeObjPtr    ptr;
  711.     long        dhSize, tblSize;
  712.     char        *cptr;
  713.     OSErr        err;
  714.  
  715.     ptr = *hndl;
  716.     tblSize = ptr->numChildren * sizeof (TreeObjHndl);
  717.     dhSize  = sizeof(TreeObj) + ptr->dataSize;
  718.  
  719.     if (!(delta & 0x80000000L)) {
  720.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  721.         if (err = MemError()) return(err);
  722.         ptr = *hndl;
  723.     }
  724.  
  725.     cptr = (char *)ptr + dhSize;
  726.     BlockMove(cptr, cptr + delta, tblSize);
  727.     ptr->dataSize += delta;
  728.  
  729.     if (delta & 0x80000000L)
  730.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  731.  
  732.     return(noErr);
  733. }
  734.  
  735.  
  736.  
  737. /**********************************************************************/
  738.  
  739.  
  740.  
  741. /* Sets the data size to newSize. */
  742.  
  743. #pragma segment File
  744. OSErr    SetDataSize(TreeObjHndl hndl, long newSize)
  745. {
  746.     return(AdjustDataSize(hndl, newSize - (*hndl)->dataSize));
  747. }
  748.  
  749.  
  750.  
  751. /**********************************************************************/
  752.  
  753.  
  754.  
  755. /* Slides some of the data in the data portion of the object starting at
  756. ** 'offset' by a 'delta' amount, either forward or backward, depending
  757. ** on the sign of 'delta'. */
  758.  
  759. #pragma segment File
  760. OSErr    SlideData(TreeObjHndl hndl, long offset, long delta)
  761. {
  762.     long    dhSize;
  763.     char    *cptr;
  764.     OSErr    err;
  765.  
  766.     if (!(delta & 0x80000000L))
  767.         if (err = AdjustDataSize(hndl, delta)) return(err);
  768.  
  769.     cptr = (char *)GetDataPtr(hndl) + offset;
  770.     dhSize = sizeof(TreeObj) + (*hndl)->dataSize;
  771.     BlockMove(cptr, cptr + delta, dhSize - offset);
  772.  
  773.     if (delta & 0x80000000L)
  774.         AdjustDataSize(hndl, delta);
  775.  
  776.     return(noErr);
  777. }
  778.  
  779.  
  780.  
  781. /**********************************************************************/
  782.  
  783.  
  784.  
  785. /* Returns a pointer to the beginning of the object's data area.
  786. ** THIS DOES NOT LOCK THE HANDLE!!  The pointer may become invalid
  787. ** if memory moves. */
  788.  
  789. #pragma segment File
  790. void    *GetDataPtr(TreeObjHndl hndl)
  791. {
  792.     return(*(char **)hndl + sizeof(TreeObj));
  793. }
  794.  
  795.  
  796.  
  797. /**********************************************************************/
  798. /**********************************************************************/
  799.  
  800.  
  801.  
  802. /* Given an open file that has been previously written via WriteTree, this function
  803. ** is called to read the file data into ram.  The root object for the file is already
  804. ** created by InitDocument.  The file must be open, and the file position must be
  805. ** set to the byte location where the root object of the file starts.  Once this is
  806. ** so, just call this function with a reference to the root object and the open file
  807. ** refrence number. */
  808.  
  809. #pragma segment File
  810. OSErr    ReadTree(TreeObjHndl hndl, short fileRefNum)
  811. {
  812.     OSErr    err;
  813.  
  814.     if (err = ReadBranch(hndl, fileRefNum)) {
  815.         DisposeObjAndOffspring(hndl);
  816.         *hndl = nil;
  817.     }
  818.     else {
  819.         DoNumberTree(hndl);
  820.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  821.     }
  822.  
  823.     return(err);
  824. }
  825.  
  826.  
  827.  
  828. /**********************************************************************/
  829.  
  830.  
  831.  
  832. /* This is an internal function for recursively reading the data from the
  833. ** open file. */
  834.  
  835. #pragma segment File
  836. OSErr    ReadBranch(TreeObjHndl hndl, short fileRefNum)
  837. {
  838.     TreeObjHndl        chndl;
  839.     TreeObjProcPtr    proc;
  840.     short            numChildren, cnum;
  841.     OSErr            err;
  842.  
  843.     if (err = ReadTreeObjHeader(hndl, fileRefNum)) {
  844.         (*hndl)->numChildren = 0;    /* So we can dispose the portion that was read. */
  845.         return(err);
  846.     }
  847.  
  848.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  849.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  850.  
  851.     if (proc = gTreeObjMethods[(*hndl)->type])
  852.         err = (*proc)(hndl, FREADMESSAGE, fileRefNum);
  853.     else
  854.         err = ReadTreeObjData(hndl, fileRefNum);
  855.  
  856.     if (err) return(err);
  857.  
  858.     for (cnum = 0; cnum < numChildren; ++cnum) {
  859.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  860.         if (InsertChildHndl(hndl, chndl, cnum)) {
  861.             DisposeObjAndOffspring(chndl);
  862.             return(memFullErr);
  863.         }
  864.         if (err = ReadBranch(chndl, fileRefNum)) return(err);
  865.     }
  866.  
  867.     return(noErr);
  868. }
  869.  
  870.  
  871.  
  872. /**********************************************************************/
  873.  
  874.  
  875.  
  876. #pragma segment File
  877. OSErr    ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  878. {
  879.     TreeObj        header;
  880.     TreeObjHndl    parent;
  881.     OSErr        err;
  882.     long        count;
  883.  
  884.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  885.     if (!(err = FSRead(fileRefNum, &count, &header))) {
  886.         parent = (*hndl)->parent;
  887.         **hndl = header;
  888.         (*hndl)->parent = parent;
  889.     }
  890.  
  891.     return(err);
  892. }
  893.  
  894.  
  895.  
  896. /**********************************************************************/
  897.  
  898.  
  899.  
  900. /* Read in dataSize number of bytes into the object. */
  901.  
  902. #pragma segment File
  903. OSErr    ReadTreeObjData(TreeObjHndl hndl, short fileRefNum)
  904. {
  905.     long    dataSize;
  906.     OSErr    err;
  907.     char    hstate;
  908.     Ptr        dataPtr;
  909.  
  910.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize))) {
  911.         hstate = HGetState((Handle)hndl);
  912.         HLock((Handle)hndl);
  913.         dataPtr = GetDataPtr(hndl);
  914.         err     = FSRead(fileRefNum, &dataSize, dataPtr);
  915.         HSetState((Handle)hndl, hstate);
  916.     }
  917.  
  918.     return(err);
  919. }
  920.  
  921.  
  922.  
  923. /**********************************************************************/
  924.  
  925.  
  926.  
  927. /* Given an open file that is ready to be written to, this function is called to
  928. ** write the file tree to the designated file. */
  929.  
  930. #pragma segment File
  931. OSErr    WriteTree(TreeObjHndl hndl, short fileRefNum)
  932. {
  933.     TreeObjProcPtr    proc;
  934.     short            cnum;
  935.     OSErr            err;
  936.  
  937.     err = WriteTreeObjHeader(hndl, fileRefNum);
  938.     if (!err) {
  939.  
  940.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  941.             /* Ready data to be written to file.  Any references to handles are invalid
  942.             ** when written to disk.  These need to be converted to a reference that
  943.             ** makes sense when read in from disk when a file is opened.  The standard
  944.             ** way to do this is to convert the handle reference to a tree-obj-number
  945.             ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  946.             ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  947.             ** giving each handle a unique id number. */
  948.  
  949.         if (proc = gTreeObjMethods[(*hndl)->type])
  950.             err = (*proc)(hndl, FWRITEMESSAGE, fileRefNum);
  951.         else
  952.             err = WriteTreeObjData(hndl, fileRefNum);
  953.  
  954.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  955.             /* Undo any id references back to handle references. */
  956.     }
  957.  
  958.  
  959.     if (!err) {
  960.         for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  961.             if (err = WriteTree(GetChildHndl(hndl, cnum), fileRefNum)) break;
  962.     }
  963.  
  964.     return(err);
  965. }
  966.  
  967.  
  968.  
  969. /**********************************************************************/
  970.  
  971.  
  972.  
  973. #pragma segment File
  974. OSErr    WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  975. {
  976.     TreeObj    header;
  977.     OSErr    err;
  978.     long    count;
  979.  
  980.     header = **hndl;
  981.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  982.     err = FSWrite(fileRefNum, &count, &header);
  983.  
  984.     return(err);
  985. }
  986.  
  987.  
  988.  
  989. /**********************************************************************/
  990.  
  991.  
  992.  
  993. /* Write out dataSize number of bytes from the object. */
  994.  
  995. #pragma segment File
  996. OSErr    WriteTreeObjData(TreeObjHndl hndl, short fileRefNum)
  997. {
  998.     long    dataSize;
  999.     OSErr    err;
  1000.     char    hstate;
  1001.     Ptr        dataPtr;
  1002.  
  1003.     dataSize = (*hndl)->dataSize;
  1004.  
  1005.     hstate  = HGetState((Handle)hndl);
  1006.     HLock((Handle)hndl);
  1007.     dataPtr = GetDataPtr(hndl);
  1008.     err     = FSWrite(fileRefNum, &dataSize, dataPtr);
  1009.     HSetState((Handle)hndl, hstate);
  1010.  
  1011.     return(err);
  1012. }
  1013.  
  1014.  
  1015.  
  1016. /**********************************************************************/
  1017.  
  1018.  
  1019.  
  1020. /* Call the object for each member of the tree (or branch) starting
  1021. ** from the back of the tree working forward. */
  1022.  
  1023. #pragma segment File
  1024. void    DoBTreeMethod(TreeObjHndl hndl, short message, long data)
  1025. {
  1026.     short    cnum;
  1027.  
  1028.     DoTreeObjMethod(hndl, message, data);
  1029.  
  1030.     for (cnum = (*hndl)->numChildren; cnum;)
  1031.         DoBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1032. }
  1033.  
  1034.  
  1035.  
  1036. /**********************************************************************/
  1037.  
  1038.  
  1039.  
  1040. /* Call the object for each member of the tree (or branch) starting
  1041. ** from the front of the tree working backward. */
  1042.  
  1043. #pragma segment File
  1044. void    DoFTreeMethod(TreeObjHndl hndl, short message, long data)
  1045. {
  1046.     short    cnum;
  1047.  
  1048.     DoTreeObjMethod(hndl, message, data);
  1049.  
  1050.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  1051.         DoFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1052. }
  1053.  
  1054.  
  1055.  
  1056. /**********************************************************************/
  1057.  
  1058.  
  1059.  
  1060. /* If the object has a method procedure, call it.  If no method procedure,
  1061. ** then do nothing. */
  1062.  
  1063. #pragma segment File
  1064. long    DoTreeObjMethod(TreeObjHndl hndl, short message, long data)
  1065. {
  1066.     TreeObjProcPtr    proc;
  1067.  
  1068.     if (proc = gTreeObjMethods[(*hndl)->type]) return((*proc)(hndl, message, data));
  1069.     return(0);
  1070. }
  1071.  
  1072.  
  1073.  
  1074. /**********************************************************************/
  1075.  
  1076.  
  1077.  
  1078. /* Number each member in the tree with a unique treeID.  The tree is number
  1079. ** sequentially from front to back.  The first treeID number is 1.  0 is
  1080. ** reserved for Hndl2ID/ID2Hndl conversions where it is possible that the
  1081. ** handle value is nil.  The nil handle will convert to 0, and convert back
  1082. ** to nil. */
  1083.  
  1084. #pragma segment File
  1085. static void    DoNumberTree0(TreeObjHndl hndl);
  1086. void        DoNumberTree(TreeObjHndl hndl)
  1087. {
  1088.     DoNumberTree0(GetRootHndl(hndl));
  1089. }
  1090.  
  1091.  
  1092.  
  1093. /**********************************************************************/
  1094.  
  1095.  
  1096.  
  1097. #pragma segment File
  1098. void    DoNumberTree0(TreeObjHndl hndl)
  1099. {
  1100.     short            cnum;
  1101.     static short    nodeNum;
  1102.  
  1103.     if (!(*hndl)->parent)
  1104.         nodeNum = 0;
  1105.     (*hndl)->treeID = ++nodeNum;
  1106.  
  1107.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  1108.         DoNumberTree0(GetChildHndl(hndl, cnum));
  1109. }
  1110.  
  1111.  
  1112.  
  1113. /**********************************************************************/
  1114.  
  1115.  
  1116.  
  1117. /* This function is used to convert a handle reference into a treeID reference.
  1118. ** A pointer to the handle reference is passed in.  Typical usage will be where
  1119. ** a handle object has a reference to another handle object.  Handle object
  1120. ** references aren't meaningful when saved to disk, and therefore don't persist
  1121. ** in their native form.  These handle references need to be converted into
  1122. ** a treeID reference so that they can be saved.
  1123. ** The tree first needs to be numbered so that the treeID references are unique
  1124. ** and meaningful.  The tree is numbered by first calling DoNumberTree().  It
  1125. ** numbers all the members of the tree hierarchy uniquely and sequentially. */
  1126.  
  1127. #pragma segment File
  1128. void    Hndl2ID(TreeObjHndl *hndl)
  1129. {
  1130.     if (*hndl)
  1131.         *hndl = (TreeObjHndl)(**hndl)->treeID;
  1132. }
  1133.  
  1134.  
  1135.  
  1136. /**********************************************************************/
  1137.  
  1138.  
  1139.  
  1140. /* Given a tree object ID and a reference object (any member of the tree),
  1141. ** return the cooresponding object handle.  DoNumberTree() must be called
  1142. ** prior to using this function, and after the last change to the tree, as
  1143. ** it generates the object treeID numbers for the entire tree. */
  1144.  
  1145. #pragma segment File
  1146. void    ID2Hndl(TreeObjHndl refHndl, TreeObjHndl *hndl)
  1147. {
  1148.     short        cnum;
  1149.     TreeObjHndl    chndl;
  1150.  
  1151.     if ((!refHndl) || (!*hndl)) return;
  1152.  
  1153.     refHndl = GetRootHndl(refHndl);
  1154.     for (;;) {
  1155.         if ((*refHndl)->treeID == (long)*hndl) {
  1156.             *hndl = refHndl;
  1157.             return;
  1158.         }
  1159.         if (!(cnum = (*refHndl)->numChildren)) return;
  1160.         for (; cnum;) {
  1161.             chndl = GetChildHndl(refHndl, --cnum);
  1162.             if ((*chndl)->treeID <= (long)*hndl) {
  1163.                 refHndl = chndl;
  1164.                 break;
  1165.             }
  1166.         }
  1167.     }
  1168. }
  1169.  
  1170.  
  1171.  
  1172. /**********************************************************************/
  1173. /**********************************************************************/
  1174. /**********************************************************************/
  1175.  
  1176.  
  1177.  
  1178. /* Given an object handle, return the undo handle. */
  1179.  
  1180. #pragma segment File
  1181. TreeObjHndl    GetUndoHndl(TreeObjHndl undo)
  1182. {
  1183.     for (; (*undo)->parent; undo = (*undo)->parent);
  1184.     if ((*undo)->type == ROOTOBJ)
  1185.         undo = mDerefRoot(undo)->undo;
  1186.     return(undo);
  1187. }
  1188.  
  1189.  
  1190.  
  1191. /**********************************************************************/
  1192.  
  1193.  
  1194.  
  1195. /* Used to close out an old undo task.  Closing out the old task means that
  1196. ** any editing to the document will be recorded into a new undo task.  Use
  1197. ** this to separate two edits of the same edit type that would otherwise
  1198. ** get recorded into the same undo task. */
  1199.  
  1200. #pragma segment File
  1201. void    NewUndo(TreeObjHndl hndl)
  1202. {
  1203.     TreeObjHndl    undo;
  1204.  
  1205.     undo = GetUndoHndl(hndl);
  1206.     mDerefUndo(undo)->lastEditType = NO_EDIT;
  1207.     mDerefUndo(undo)->disabled     = false;
  1208. }
  1209.  
  1210.  
  1211.  
  1212. /**********************************************************************/
  1213.  
  1214.  
  1215.  
  1216. /* If an edit fails, it can be backed out of by calling this.  All edits
  1217. ** that were done will be undone, thus recovering the state of the
  1218. ** document prior to the edit. */
  1219.  
  1220. #pragma segment File
  1221. void    RevertEdit(TreeObjHndl hndl)
  1222. {
  1223.     TreeObjHndl    root;
  1224.     FileRecHndl    frHndl;
  1225.     Boolean        docDirty;
  1226.  
  1227.     root     = GetRootHndl(hndl);
  1228.     frHndl   = mDerefRoot(root)->frHndl;
  1229.     docDirty = (*frHndl)->fileState.docDirty;
  1230.  
  1231.     DoUndoTask(hndl, DOUNDO, false);
  1232.         /* Use the undo mechanism to back out of the edit task. */
  1233.  
  1234.     DisposeChild(NO_EDIT, GetUndoHndl(hndl), -1);
  1235.         /* Get rid of the undo that was just used to revert.
  1236.         ** Leave the rest of the undos. */
  1237.  
  1238.     (*frHndl)->fileState.docDirty = docDirty;
  1239. }
  1240.  
  1241.  
  1242.  
  1243. /**********************************************************************/
  1244.  
  1245.  
  1246.  
  1247. /* Call this to undo or redo editing to the document.  If redo is false,
  1248. ** the it is an undo task. */
  1249.  
  1250. #pragma segment File
  1251. void    DoUndoTask(TreeObjHndl hndl, Boolean redo, Boolean fixup)
  1252. {
  1253.     FileRecHndl    frHndl;
  1254.     TreeObjHndl    undo, undoTask, undoPart;
  1255.     short        numUndos, undoDepth, undoPartNum;
  1256.     short        beg, end, inc;
  1257.     Point        contOrg;
  1258.  
  1259.     NewUndo(undo = GetUndoHndl(hndl));
  1260.  
  1261.     numUndos  = (*undo)->numChildren;
  1262.     undoDepth = mDerefUndo(undo)->undoDepth;
  1263.  
  1264.     if ((redo) && (numUndos == undoDepth)) return;
  1265.     if (!(numUndos | undoDepth)) return;
  1266.  
  1267.     frHndl = mDerefUndo(undo)->frHndl;
  1268.  
  1269.     if (redo) undoDepth++;
  1270.     undoTask = GetChildHndl(undo, --undoDepth);
  1271.  
  1272.     if (!redo) {
  1273.         GetContentOrigin((*frHndl)->fileState.window, &contOrg);
  1274.         mDerefUndoTask(undoTask)->redoOrigin = contOrg;
  1275.         contOrg = mDerefUndoTask(undoTask)->undoOrigin;
  1276.     }
  1277.     else contOrg = mDerefUndoTask(undoTask)->redoOrigin;
  1278.  
  1279.     if (fixup)
  1280.         DoUndoFixup(frHndl, contOrg, 0);
  1281.             /* Prepare for undo task, such as deselecting the current selection so
  1282.             ** that the undone stuff can be displayed as the only selected stuff. */
  1283.  
  1284.     beg = (*undoTask)->numChildren - 1;
  1285.     end = -1;
  1286.     inc = -1;
  1287.     if (redo) {
  1288.         end = ++beg;
  1289.         beg = 0;
  1290.         inc = 1;
  1291.     }
  1292.  
  1293.     for (undoPartNum = beg; undoPartNum != end; undoPartNum += inc) {
  1294.         undoPart = GetChildHndl(undoTask, undoPartNum);
  1295.         switch(mDerefUndoPart(undoPart)->actionType) {
  1296.             case NEW_CHILD:
  1297.                 UndoNewChild(undoPart);
  1298.                 break;
  1299.             case DISPOSE_CHILD:
  1300.                 UndoDisposeChild(undoPart);
  1301.                 break;
  1302.             case MOVE_CHILD:
  1303.                 UndoMoveChild(undoPart);
  1304.                 break;
  1305.             case CHANGE_CHILD:
  1306.                 UndoModifyChild(undoPart);
  1307.                 break;
  1308.             case SWAP_CHILDREN:
  1309.                 UndoSwapChildren(undoPart);
  1310.                 break;
  1311.         }
  1312.     }
  1313.  
  1314.     inc = -1;
  1315.     if (redo)
  1316.         inc = 1;
  1317.  
  1318.     mDerefUndo(undo)->undoDepth += inc;
  1319.  
  1320.     if (fixup)
  1321.         DoUndoFixup(frHndl, contOrg, 1);
  1322.             /* Clean up and redisplay after undo task. */
  1323.  
  1324.     SetDocDirty(frHndl);
  1325. }
  1326.  
  1327.  
  1328.  
  1329. /**********************************************************************/
  1330.  
  1331.  
  1332.  
  1333. TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType)
  1334. {
  1335.     TreeObjHndl        lastTaskHndl;
  1336.     short            lastEditType, undoDepth, addNewUndo, numUndoLevels, maxNumUndos;
  1337.     Point            contOrg;
  1338.     FileRecHndl        frHndl;
  1339.     WindowPtr        window;
  1340.  
  1341.     if (!(maxNumUndos = mDerefUndo(undo)->maxNumUndos)) return(nil);
  1342.  
  1343.     lastEditType = mDerefUndo(undo)->lastEditType;
  1344.     undoDepth    = mDerefUndo(undo)->undoDepth;
  1345.  
  1346.     addNewUndo = false;
  1347.     if (editType != lastEditType)
  1348.         addNewUndo = true;
  1349.     if (!(numUndoLevels = (*undo)->numChildren))
  1350.         addNewUndo = true;
  1351.  
  1352.     while (undoDepth < numUndoLevels) {
  1353.         DisposeChild(NO_EDIT, undo, --numUndoLevels);
  1354.         addNewUndo = true;        /* Flushing old also indicates a new undo. */
  1355.     }                            /* undoDepth now is the same as numUndoLevels. */
  1356.  
  1357.     lastTaskHndl = nil;
  1358.     if (!addNewUndo) {
  1359.         lastTaskHndl = GetChildHndl(undo, -1);        /* Get last child handle. */
  1360.         if ((editType) && (editType != mDerefUndoTask(lastTaskHndl)->editType))
  1361.             lastTaskHndl = nil;
  1362.     }
  1363.  
  1364.     if (!lastTaskHndl) {
  1365.         while (numUndoLevels >= maxNumUndos) {
  1366.             DisposeChild(NO_EDIT, undo, 0);
  1367.             mDerefUndo(undo)->undoDepth--;
  1368.             numUndoLevels--;
  1369.         }    /* Restrict number of undos to designated level. */
  1370.  
  1371.         if (lastTaskHndl = NewChild(NO_EDIT, undo, numUndoLevels, UNDOTASKOBJ, 0)) {
  1372.             mDerefUndo(undo)->lastEditType = editType;
  1373.             mDerefUndo(undo)->undoDepth++;
  1374.             frHndl = mDerefUndo(undo)->frHndl;
  1375.             window = (*frHndl)->fileState.window;
  1376.             GetContentOrigin(window, &contOrg);
  1377.             mDerefUndoTask(lastTaskHndl)->editType   = editType;
  1378.             mDerefUndoTask(lastTaskHndl)->undoOrigin = contOrg;
  1379.             mDerefUndoTask(lastTaskHndl)->redoOrigin = contOrg;
  1380.         }
  1381.     }
  1382.  
  1383.     return(lastTaskHndl);
  1384. }
  1385.  
  1386.  
  1387.  
  1388. /**********************************************************************/
  1389.  
  1390.  
  1391.  
  1392. #pragma segment File
  1393. TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short scnum,
  1394.                         TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  1395. {
  1396.     TreeObjHndl    partHndl;
  1397.     UndoPartObj    *partPtr;
  1398.  
  1399.     partHndl = NewChild(NO_EDIT, taskHndl, -1, UNDOPARTOBJ, 0);    /* Add new child to end. */
  1400.     if (partHndl) {
  1401.         partPtr = GetDataPtr(partHndl);
  1402.         partPtr->actionType = action;
  1403.         partPtr->shndl      = shndl;
  1404.         partPtr->scnum      = scnum;
  1405.         partPtr->dhndl      = dhndl;
  1406.         partPtr->dcnum      = dcnum;
  1407.         partPtr->deepCopy   = deepCopy;
  1408.     }
  1409.  
  1410.     return(partHndl);
  1411. }
  1412.  
  1413.  
  1414.  
  1415. /**********************************************************************/
  1416.  
  1417.  
  1418.  
  1419. #pragma segment File
  1420. OSErr    PostNewChild(short editType, TreeObjHndl phndl, short cnum)
  1421. {
  1422.     TreeObjHndl    undo, task;
  1423.  
  1424.     if (!editType) return(noErr);
  1425.  
  1426.     undo = GetUndoHndl(phndl);
  1427.     if (mDerefUndo(undo)->disabled) return(noErr);
  1428.  
  1429.     if (task = GetUndoTaskHndl(undo, editType))
  1430.         if (NewUndoPart(task, NEW_CHILD, phndl, cnum, nil, 0, false)) return(noErr);
  1431.  
  1432.     return(memFullErr);
  1433. }
  1434.  
  1435.  
  1436.  
  1437. /**********************************************************************/
  1438.  
  1439.  
  1440.  
  1441. #pragma segment File
  1442. OSErr    PostDisposeChild(short editType, TreeObjHndl phndl, short cnum)
  1443. {
  1444.     TreeObjHndl    undo, task, part;
  1445.  
  1446.     if (!editType) return(noErr);
  1447.  
  1448.     undo = GetUndoHndl(phndl);
  1449.     if (mDerefUndo(undo)->disabled) return(noErr);
  1450.  
  1451.     if (task = GetUndoTaskHndl(undo, editType)) {
  1452.         part = NewUndoPart(task, DISPOSE_CHILD, phndl, cnum, nil, 0, false);
  1453.         if (part) {
  1454.             MoveChild(NO_EDIT, phndl, cnum, part, -1);
  1455.             return(noErr);
  1456.         }
  1457.     }
  1458.  
  1459.     return(memFullErr);
  1460. }
  1461.  
  1462.  
  1463.  
  1464. /**********************************************************************/
  1465.  
  1466.  
  1467.  
  1468. #pragma segment File
  1469. void    PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  1470. {
  1471.     TreeObjHndl    undo, task;
  1472.  
  1473.     if (editType) {
  1474.         undo = GetUndoHndl(shndl);
  1475.         if (!mDerefUndo(undo)->disabled)
  1476.             if (task = GetUndoTaskHndl(undo, editType))
  1477.                 NewUndoPart(task, MOVE_CHILD, shndl, scnum, dhndl, dcnum, false);
  1478.     }
  1479. }
  1480.  
  1481.  
  1482.  
  1483. /**********************************************************************/
  1484.  
  1485.  
  1486.  
  1487. #pragma segment File
  1488. void    PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  1489. {
  1490.     TreeObjHndl    undo, task;
  1491.  
  1492.     if (editType) {
  1493.         undo = GetUndoHndl(hndla);
  1494.         if (!mDerefUndo(undo)->disabled)
  1495.             if (task = GetUndoTaskHndl(undo, editType))
  1496.                 NewUndoPart(task, SWAP_CHILDREN, hndla, cnuma, hndlb, cnumb, false);
  1497.  
  1498.     }
  1499. }
  1500.  
  1501.  
  1502.  
  1503. /**********************************************************************/
  1504.  
  1505.  
  1506.  
  1507. #pragma segment File
  1508. void    UndoNewChild(TreeObjHndl undoPart)
  1509. {
  1510.     TreeObjHndl    shndl, chndl;
  1511.     short        scnum;
  1512.  
  1513.     shndl = mDerefUndoPart(undoPart)->shndl;
  1514.     scnum = mDerefUndoPart(undoPart)->scnum;
  1515.     mDerefUndoPart(undoPart)->actionType = DISPOSE_CHILD;
  1516.  
  1517.     chndl = GetChildHndl(shndl, scnum);
  1518.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  1519.     MoveChild(NO_EDIT, shndl, scnum, undoPart, 0);
  1520. }
  1521.  
  1522.  
  1523.  
  1524. /**********************************************************************/
  1525.  
  1526.  
  1527.  
  1528. #pragma segment File
  1529. void    UndoDisposeChild(TreeObjHndl undoPart)
  1530. {
  1531.     TreeObjHndl    shndl, chndl;
  1532.     short        scnum;
  1533.  
  1534.     shndl = mDerefUndoPart(undoPart)->shndl;
  1535.     scnum = mDerefUndoPart(undoPart)->scnum;
  1536.     mDerefUndoPart(undoPart)->actionType = NEW_CHILD;
  1537.  
  1538.     chndl = GetChildHndl(undoPart, 0);
  1539.     MoveChild(NO_EDIT, undoPart, 0, shndl, scnum);
  1540.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  1541. }
  1542.  
  1543.  
  1544.  
  1545. /**********************************************************************/
  1546.  
  1547.  
  1548.  
  1549. #pragma segment File
  1550. void    UndoMoveChild(TreeObjHndl undoPart)
  1551. {
  1552.     TreeObjHndl    shndl, dhndl;
  1553.     short        scnum, dcnum;
  1554.  
  1555.     shndl = mDerefUndoPart(undoPart)->shndl;
  1556.     scnum = mDerefUndoPart(undoPart)->scnum;
  1557.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1558.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1559.  
  1560.     DoTreeObjMethod(GetChildHndl(dhndl, dcnum), UNDOMESSAGE, UNDOFROMDOC);
  1561.     MoveChild(NO_EDIT, dhndl, dcnum, shndl, scnum);
  1562.     DoTreeObjMethod(GetChildHndl(shndl, scnum), UNDOMESSAGE, UNDOTODOC);
  1563.  
  1564.     mDerefUndoPart(undoPart)->shndl = dhndl;
  1565.     mDerefUndoPart(undoPart)->scnum = dcnum;
  1566.     mDerefUndoPart(undoPart)->dhndl = shndl;
  1567.     mDerefUndoPart(undoPart)->dcnum = scnum;
  1568. }
  1569.  
  1570.  
  1571.  
  1572. /**********************************************************************/
  1573.  
  1574.  
  1575.  
  1576. #pragma segment File
  1577. void    UndoModifyChild(TreeObjHndl undoPart)
  1578. {
  1579.     TreeObjHndl    shndl, dchndl, uchndl;
  1580.     short        scnum;
  1581.  
  1582.     shndl = mDerefUndoPart(undoPart)->shndl;
  1583.     scnum = mDerefUndoPart(undoPart)->scnum;
  1584.  
  1585.     dchndl = GetChildHndl(shndl, scnum);    /* Document child handle. */
  1586.     uchndl = GetChildHndl(undoPart, 0);        /* Undo child handle. */
  1587.  
  1588.     UndoModifyChildren(dchndl, uchndl, mDerefUndoPart(undoPart)->deepCopy);
  1589. }
  1590.  
  1591.  
  1592.  
  1593. /**********************************************************************/
  1594.  
  1595.  
  1596.  
  1597. #pragma segment File
  1598. void    UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy)
  1599. {
  1600.     TreeObjHndl    dchndl, uchndl;
  1601.     short        i;
  1602.  
  1603.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOFROMDOC);    /* Old data leaving document. */
  1604.     SwapChildData(dhndl, uhndl);
  1605.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOTODOC);        /* New data entering document. */
  1606.  
  1607.     if (deepCopy) {
  1608.         for (i = (*dhndl)->numChildren; i;) {
  1609.             dchndl = GetChildHndl(dhndl, --i);
  1610.             uchndl = GetChildHndl(uhndl, i);
  1611.             UndoModifyChildren(dchndl, uchndl, deepCopy);
  1612.         }
  1613.     }
  1614. }
  1615.  
  1616.  
  1617.  
  1618. /**********************************************************************/
  1619.  
  1620.  
  1621.  
  1622. #pragma segment File
  1623. void    UndoSwapChildren(TreeObjHndl undoPart)
  1624. {
  1625.     TreeObjHndl    shndl, dhndl, schndl, dchndl;
  1626.     short        scnum, dcnum;
  1627.  
  1628.     shndl = mDerefUndoPart(undoPart)->shndl;
  1629.     scnum = mDerefUndoPart(undoPart)->scnum;
  1630.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1631.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1632.  
  1633.     schndl = GetChildHndl(shndl, scnum);
  1634.     dchndl = GetChildHndl(dhndl, dcnum);
  1635.  
  1636.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOFROMDOC);
  1637.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOFROMDOC);
  1638.  
  1639.     SwapChildren(NO_EDIT, shndl, scnum, dhndl, dcnum);
  1640.  
  1641.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOTODOC);
  1642.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOTODOC);
  1643. }
  1644.  
  1645.  
  1646.  
  1647. /**********************************************************************/
  1648.  
  1649.  
  1650.  
  1651. /* Dispose of all undo information and prevent further undo collection.
  1652. ** Calling NewUndo() will re-enable undo collection. */
  1653.  
  1654. #pragma segment File
  1655. void    DisableUndo(TreeObjHndl hndl)
  1656. {
  1657.     TreeObjHndl    undo;
  1658.  
  1659.     undo = GetUndoHndl(hndl);
  1660.     while ((*undo)->numChildren) DisposeChild(NO_EDIT, undo, 0);
  1661.  
  1662.     mDerefUndo(undo)->undoDepth = 0;
  1663.     mDerefUndo(undo)->disabled  = true;
  1664. }
  1665.  
  1666.  
  1667.  
  1668. /**********************************************************************/
  1669.  
  1670.  
  1671.  
  1672. /* Dispose of all undo information and prevent further undo collection.
  1673. ** Calling NewUndo() will re-enable undo collection. */
  1674.  
  1675. #pragma segment File
  1676. void    DisposeUndos(TreeObjHndl hndl)
  1677. {
  1678.     DisableUndo(hndl);
  1679.     NewUndo(hndl);
  1680. }
  1681.  
  1682.  
  1683.  
  1684. /**********************************************************************/
  1685.  
  1686.  
  1687.  
  1688. /* Dispose of all undo information except the current undo.  The current undo
  1689. ** may still be active, and it may be needed to back out of an edit operation. */
  1690.  
  1691. #pragma segment File
  1692. Boolean    PurgeUndo(TreeObjHndl hndl)
  1693. {
  1694.     TreeObjHndl    undo;
  1695.     Boolean        didPurge;
  1696.  
  1697.     undo = GetUndoHndl(hndl);
  1698.     didPurge = false;
  1699.     while ((*undo)->numChildren > 1) {
  1700.         DisposeChild(NO_EDIT, undo, 0);
  1701.         didPurge = true;
  1702.     }
  1703.  
  1704.     return(didPurge);
  1705. }
  1706.  
  1707.  
  1708.  
  1709. /**********************************************************************/
  1710.  
  1711.  
  1712.  
  1713. #pragma segment File
  1714. void    GetUndoInfo(FileRecHndl frHndl, short *undoDepth, short *numUndos)
  1715. {
  1716.     TreeObjHndl    undo;
  1717.  
  1718.     *undoDepth = *numUndos = 0;
  1719.     if ((*frHndl)->fileState.readOnly) return;
  1720.  
  1721.     undo = GetUndoHndl((*frHndl)->d.doc.root);
  1722.     if (!mDerefUndo(undo)->disabled) {
  1723.         *undoDepth = mDerefUndo(undo)->undoDepth;
  1724.         *numUndos  = (*undo)->numChildren;
  1725.     }
  1726. }
  1727.  
  1728.  
  1729.  
  1730.